/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.dusa.worklog.viewer;

import cz.dusa.worklog.controller.WorklogDataException;
import cz.dusa.worklog.controller.WorklogDataListener;
import cz.dusa.worklog.controller.WorklogDataManager;
import cz.dusa.worklog.datalib.Project;
import cz.dusa.worklog.datalib.WorkDay;
import cz.dusa.worklog.datalib.WorklogData;
import java.io.IOException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.view.JasperViewer;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
//import org.openide.util.ImageUtilities;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.awt.UndoRedo;
import org.openide.cookies.SaveCookie;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(dtd = "-//cz.dusa.worklog.viewer//ProjectEditor//EN",
autostore = false)
public final class ProjectEditorTopComponent extends TopComponent implements TableModelListener, WorklogDataListener<Project> {

    private static ProjectEditorTopComponent instance;
    /** path to the icon used by the component and its open action */
    static final String ICON_PATH = "cz/dusa/worklog/viewer/FolderEdit.png";
    private static final String PREFERRED_ID = "ProjectEditorTopComponent";
    private Project project = null;
    private UndoRedo.Manager manager = new UndoRedo.Manager();
    private final SaveCookieImpl impl;
    private final InstanceContent content;

    @Deprecated
    public ProjectEditorTopComponent() {
        throw new UnsupportedOperationException("Do not use default constructor of ProjectEditorTopComponent. You can use ProjectEditorTopComponent(Project)");
    }

    public ProjectEditorTopComponent(Project project) {
        initComponents();

        setToolTipText(NbBundle.getMessage(ProjectEditorTopComponent.class, "HINT_ProjectEditorTopComponent"));
        setIcon(ImageUtilities.loadImage(ICON_PATH, true));

        JTextField[] textfields = new JTextField[]{name};
        for (JTextField textfield : textfields) {
            textfield.getDocument().addUndoableEditListener(manager);
            textfield.getDocument().addDocumentListener(new DocumentListener() {

                public void insertUpdate(DocumentEvent arg0) {
                    fire(true);
                }

                public void removeUpdate(DocumentEvent arg0) {
                    fire(true);
                }

                public void changedUpdate(DocumentEvent arg0) {
                    fire(true);
                }
            });
        }
        //Create a new instance of our SaveCookie implementation:
        impl = new SaveCookieImpl();

        //Create a new instance of our dynamic object:
        content = new InstanceContent();

        //Add the dynamic object to the TopComponent Lookup:
        associateLookup(new AbstractLookup(content));

        setProject(project);
    }

    public void fire(boolean modified) {
        if (modified) {
            //If the text is modified,
            //we add SaveCookie impl to Lookup:
            content.add(impl);
        } else {
            //Otherwise, we remove the SaveCookie impl from the lookup:
            content.remove(impl);
        }
    }

    /**
     * @return the project
     */
    private Project getProject() {
        return project;
    }

    /**
     * @param project the project to set
     */
    private void setProject(Project project) {
        if (getProject() != null) {
            WorklogDataManager.getInstance().removeWorklogDataListener(this, Project.class, getProject().getId());
        }

        this.project = project;
        name.setText(getProject().getName());
        if (project.getName() != null && !project.getName().equals("")) {
            setName(project.getName());
        } else {
            setName(NbBundle.getMessage(ProjectEditorTopComponent.class, "Project"));
        }

        TableModel oldModel = workDays.getModel();
        if (oldModel != null) {
            oldModel.removeTableModelListener(this);
        }

        workDays.setModel(new WorkDaysTableModel(getProject().getWorkDays(), this));
        DateFormat dateRenderFormat = new SimpleDateFormat("EEE dd.MM.yyyy");
        DateFormat dateEditorFormat = new SimpleDateFormat("dd.MM.yyyy");
        TableColumn dayColumn = workDays.getColumnModel().getColumn(0);
        dayColumn.setCellEditor(new DateCellEditor(dateEditorFormat));
        dayColumn.setCellRenderer(new DateCellRenderer(dateRenderFormat));

        DateFormat timeFormat = new SimpleDateFormat("HH:mm");
        TableColumn startColumn = workDays.getColumnModel().getColumn(2);
        TableColumn endColumn = workDays.getColumnModel().getColumn(3);
        startColumn.setCellEditor(new TimeCellEditor(timeFormat));
        startColumn.setCellRenderer(new TimeCellRenderer(timeFormat));
        endColumn.setCellEditor(new TimeCellEditor(timeFormat));
        endColumn.setCellRenderer(new TimeCellRenderer(timeFormat));

        refreshTotalHours();
        fire(false);
        manager.discardAllEdits();
        WorklogDataManager.getInstance().addWorklogDataListener(this, Project.class, project.getId());
    }

    public void tableChanged(TableModelEvent tme) {
        refreshTotalHours();
        fire(true);
    }

    public void afterCreate(Project project) {
        //do nothing
    }

    public void afterUpdate(Project project) {
        setProject(project);
    }

    public void beforeDelete(Project project) {
        close();
    }

    private class SaveCookieImpl implements SaveCookie {

        @Override
        public void save() throws IOException {
            fire(false);
            getProject().setName(name.getText());
            try {
                if (getProject().getId() != null) {
                    //update of the project in the window will be made automatically by WorklogDataManager
                    //see this.afterUpdate() method;
                    WorklogDataManager.getInstance().merge(getProject());
                } else {
                    afterUpdate((Project) WorklogDataManager.getInstance().persist(getProject()));
                }
            } catch (WorklogDataException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        lblName = new javax.swing.JLabel();
        name = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        totalHours = new javax.swing.JTextField();
        jPanel2 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        workDays = new javax.swing.JTable();
        jPanel3 = new javax.swing.JPanel();
        insert = new javax.swing.JButton();
        delete = new javax.swing.JButton();
        print = new javax.swing.JButton();

        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "Project"))); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(lblName, org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "ProjectEditorTopComponent.lblName.text")); // NOI18N

        name.setText(org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "ProjectEditorTopComponent.name.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "ProjectEditorTopComponent.jLabel1.text")); // NOI18N

        totalHours.setEditable(false);
        totalHours.setText(org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "ProjectEditorTopComponent.totalHours.text")); // NOI18N
        totalHours.setBorder(null);
        totalHours.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                totalHoursActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lblName)
                    .addComponent(jLabel1))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(name, javax.swing.GroupLayout.DEFAULT_SIZE, 612, Short.MAX_VALUE)
                    .addComponent(totalHours, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblName)
                    .addComponent(name, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(totalHours, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "WorkDays"))); // NOI18N

        workDays.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {

            }
        ));
        jScrollPane1.setViewportView(workDays);

        org.openide.awt.Mnemonics.setLocalizedText(insert, org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "ProjectEditorTopComponent.insert.text")); // NOI18N
        insert.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                insertActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(delete, org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "ProjectEditorTopComponent.delete.text")); // NOI18N
        delete.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                deleteActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup()
                .addContainerGap(458, Short.MAX_VALUE)
                .addComponent(insert)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(delete)
                .addGap(10, 10, 10))
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(delete)
                .addComponent(insert))
        );

        org.openide.awt.Mnemonics.setLocalizedText(print, org.openide.util.NbBundle.getMessage(ProjectEditorTopComponent.class, "ProjectEditorTopComponent.print.text")); // NOI18N
        print.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                printActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 705, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
                        .addComponent(print)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                .addContainerGap())
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 199, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(print))
                .addContainerGap())
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    private void insertActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_insertActionPerformed
        WorkDay workDay = new WorkDay();
        workDay.setProject(project);
        getProject().getWorkDays().add(workDay);

        WorkDaysTableModel model = (WorkDaysTableModel) workDays.getModel();
        model.fireTableRowsInserted(getProject().getWorkDays().size() - 1, getProject().getWorkDays().size() - 1);

        int row = workDays.getRowCount() - 1;
        workDays.changeSelection(row, 0, false, false);
        workDays.requestFocusInWindow();

    }//GEN-LAST:event_insertActionPerformed

    private void totalHoursActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_totalHoursActionPerformed
        // TODO add your handling code here:
}//GEN-LAST:event_totalHoursActionPerformed

    private void printActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_printActionPerformed
        try {
            Map params = new HashMap();
            List<Project> projects = new ArrayList<Project>();
            projects.add(getProject());
            JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(projects);
            //JOptionPane.showMessageDialog(null, "Before Fill");
            JasperPrint report = JasperFillManager.fillReport(getClass().getResourceAsStream("project.jasper"), params, dataSource);
            //JOptionPane.showMessageDialog(null, "Before View");
            JasperViewer.viewReport(report, false);
        } catch (JRException ex) {
            Exceptions.printStackTrace(ex);
        }
    }//GEN-LAST:event_printActionPerformed

    private void deleteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteActionPerformed
        int[] selectedRows = workDays.getSelectedRows();

        if (selectedRows.length > 0) {
            if (JOptionPane.showConfirmDialog(null, NbBundle.getMessage(CustomersTopComponent.class, "DeleteWorkDaysConfirmDialogText"), NbBundle.getMessage(CustomersTopComponent.class, "DeleteWorkDaysConfirmDialogTitle"), JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
                return;
            }

            WorkDaysTableModel model = (WorkDaysTableModel) workDays.getModel();
            int counter = 0;
            try {
                for (int i : selectedRows) {
                    int row = i - counter;
                    WorkDay wd = getProject().getWorkDays().get(row);
                    getProject().getWorkDays().remove(wd);

                    if (wd.getId() != null) {
                        WorklogDataManager.getInstance().remove(wd);
                    }

                    model.fireTableRowsDeleted(row, row);
                    counter++;
                }
            } catch (WorklogDataException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    }//GEN-LAST:event_deleteActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton delete;
    private javax.swing.JButton insert;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JLabel lblName;
    private javax.swing.JTextField name;
    private javax.swing.JButton print;
    private javax.swing.JTextField totalHours;
    private javax.swing.JTable workDays;
    // End of variables declaration//GEN-END:variables

    /**
     * Gets default instance. Do not use directly: reserved for *.settings files only,
     * i.e. deserialization routines; otherwise you could get a non-deserialized instance.
     * To obtain the singleton instance, use {@link #findInstance}.
     */
    public static synchronized ProjectEditorTopComponent getDefault() {
        if (instance == null) {
            instance = new ProjectEditorTopComponent();


        }
        return instance;


    }

    /**
     * Obtain the ProjectEditorTopComponent instance. Never call {@link #getDefault} directly!
     */
    public static synchronized ProjectEditorTopComponent findInstance(Project p) {
        String preferredID = PREFERRED_ID + p.getId();

        //here was important to set the PersistenceType to PERSISTENCE_NEVER
        // (PERSISTENCE_ONLY_OPENED does not make sence because the Translator comes as an EJB and must
        //be persisted there (not localy by the window manager)

        TopComponent win = WindowManager.getDefault().findTopComponent(preferredID);

        if (win != null && p.getId() == null) {
            win.close();
            win = null;
        }
        if (win == null || p.getId() == null) {
            Logger.getLogger(ProjectEditorTopComponent.class.getName()).log(
                    Level.WARNING, "Cannot find {0} component. It will not be located properly in the window system.", preferredID);
            return new ProjectEditorTopComponent(p);
        }

        if (win instanceof ProjectEditorTopComponent) {
            return (ProjectEditorTopComponent) win;
        }

        Logger.getLogger(CustomerEditorTopComponent.class.getName()).log(
                Level.WARNING, "There seem to be multiple components with the ''{0}'' ID. That is a potential source of errors and unexpected behavior.", preferredID);

        return null;
    }

    @Override
    public int getPersistenceType() {
        return TopComponent.PERSISTENCE_NEVER;
    }

    @Override
    public void componentOpened() {
        // TODO add custom code on component opening
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    Object readProperties(java.util.Properties p) {
        if (instance == null) {
            instance = this;
        }
        instance.readPropertiesImpl(p);
        return instance;
    }

    private void readPropertiesImpl(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }

    @Override
    protected String preferredID() {
        if (getProject() != null) {
            return PREFERRED_ID + getProject().getId();
        } else {
            return PREFERRED_ID;
        }
    }

    @Override
    public UndoRedo getUndoRedo() {
        return manager;
    }

    private void saveCurrentProject() {
        if (getLookup().lookup(SaveCookieImpl.class) != null) {
            try {
                impl.save();
            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    }

    @Override
    public boolean canClose() {
        saveCurrentProject();
        return true;
    }

    private void refreshTotalHours() {
        Double hours = getProject().getTotalHours();
        DecimalFormat df = new DecimalFormat("#.##");
        totalHours.setText(df.format(hours));
    }
}
